﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.ComponentModel;
using System.Web;
using System.Collections;
using System.Web.UI.HtmlControls;

[assembly: WebResource("Al.UI.Controls.Grid.Grid.js", "text/javascript")]
[assembly: WebResource("Al.UI.Controls.Grid.Grid.css", "text/javascript")]

namespace Al.UI.Controls
{
    public class Grid : DataBoundControl, INamingContainer, IPostBackDataHandler, IScriptControl
    {
        #region Member Variables

        private static readonly object EventItemCreated;
        private static readonly object EventItemDataBound;
        private GridItemCollection _rowItemCollection;
        private GridColumnCollection _columnCollection;
        private GridColumnCollection _innerColumnCollection;

        public struct Constants
        {
            public const string CellPadding = "0";
            public const string CellSpacing = "0";
            public const string RowHeaderWidth = "16px";
            public const string SelectionRectInactiveCssClass = "selectionRectInactive";
            public const string FocusRectInactiveCssClass = "focusRectInactive";
            public const string TableCssClass = "tableLayout";
            public const string RowHeaderCssClass = "rowHeader";
            public const string ColumnHeaderCssClass = "columnHeader";
            public const string EvenRowBackgroundColor = "#e0e0e0";
            public const string OddRowBackgroundColor = "#f0f0f0";
        }

        #endregion

        #region Constructors

        static Grid()
        {
            EventItemCreated = new object();
            EventItemDataBound = new object();
        }

        #endregion

        #region Public Properties

        public GridItemCollection Items
        {
            get
            {
                return this._rowItemCollection;
            }
        }

        [PersistenceMode(PersistenceMode.InnerProperty)]
        public GridColumnCollection Columns
        {
            get
            {
                if (this._columnCollection == null)
                {
                    this._columnCollection = new GridColumnCollection();
                    if (base.IsTrackingViewState)
                        ((IStateManager)this._columnCollection).TrackViewState();
                }
                return this._columnCollection;
            }
        }

        [DefaultValue(true)]
        public bool AutoGenerateColumns
        {
            get
            {
                object result = this.ViewState["AutoGenerateColumns"] ?? true;
                return (bool)result;
            }
            set
            {
                this.ViewState["AutoGenerateColumns"] = value;
            }
        }

        [DefaultValue(GridSelectionMode.None)]
        public GridSelectionMode SelectionMode
        {
            get
            {
                object result = this.ViewState["SelectionMode"];
                if (result != null)
                    return (GridSelectionMode)result;
                else
                    return GridSelectionMode.None;
            }
            set
            {
                this.ViewState["SelectionMode"] = value;
            }
        }

        #endregion

        #region Implementation

        protected override void OnPreRender(EventArgs e)
        {
            ScriptManager scriptManager = ScriptManager.GetCurrent(Page);
            scriptManager.RegisterScriptControl(this);

            base.OnPreRender(e);
        }

        protected override void OnLoad(EventArgs e)
        {
            base.OnLoad(e);
            this.RegisterCssReferences();
        }

        private void RegisterCssReferences()
        {
            string styleSheet = Page.ClientScript.GetWebResourceUrl(this.GetType(), "Al.UI.Controls.Grid.Grid.css");
            HtmlHead header = this.Page.Header;

            bool addIt = true;  // looking for this stylesheet already in the header
            foreach (Control c in header.Controls)
            {
                HtmlLink l = c as HtmlLink;
                if (null != l && styleSheet.Equals(l.Href, StringComparison.OrdinalIgnoreCase))
                {
                    addIt = false;
                    break;
                }
            }
            if (addIt)
            {
                HtmlLink link = new HtmlLink();
                link.Href = styleSheet;
                link.Attributes.Add("type", "text/css");
                link.Attributes.Add("rel", "stylesheet");
                header.Controls.Add(link);

                // ASP.NET AJAX doesn't currently send a new head element down during an async postback,
                // so we do the same thing on the client by registering the appropriate script for after
                // the update. A HiddenField is used to prevent multiple registrations.
                ScriptManager scriptManager = ScriptManager.GetCurrent(Page);
                if (scriptManager.IsInAsyncPostBack &&
                    (null == this.Page.Request.Form["__AjaxControlToolkitCssLoaded"]))
                {
                    ScriptManager.RegisterClientScriptBlock(this, this.GetType(), "RegisterCssReferences",
                        "var head = document.getElementsByTagName('HEAD')[0];" +
                        "if (head) {" +
                            "var linkElement = document.createElement('link');" +
                            "linkElement.type = 'text/css';" +
                            "linkElement.rel = 'stylesheet';" +
                            "linkElement.href = '" + styleSheet + "';" +
                            "head.appendChild(linkElement);" +
                        "}"
                        , true);
                    ScriptManager.RegisterHiddenField(this, "__AjaxControlToolkitCssLoaded", "");
                }
            }
        }

        protected override void PerformSelect()
        {
            if (!base.IsBoundUsingDataSourceID)
                OnDataBinding(EventArgs.Empty);

            base.GetData().Select(base.CreateDataSourceSelectArguments(), this.OnDataSourceViewSelectCallback);
            base.RequiresDataBinding = false;
            base.MarkAsDataBound();
            base.OnDataBound(EventArgs.Empty);
        }

        private void OnDataSourceViewSelectCallback(IEnumerable retrivedData)
        {
            if (base.IsBoundUsingDataSourceID)
                base.OnDataBinding(EventArgs.Empty);

            PerformDataBinding(retrivedData);
        }

        protected override void PerformDataBinding(IEnumerable retrivedData)
        {
            base.PerformDataBinding(retrivedData);
            this._innerColumnCollection = this.CreateColumnSet(retrivedData);
            this._rowItemCollection = new GridItemCollection();

            if (retrivedData != null)
            {
                int dataItemIndex = -1;
                int itemIndex = 0;
                foreach (object dataItem in retrivedData)
                {
                    GridItem item = this.CreateItem(itemIndex, dataItem);
                    this._rowItemCollection.Add(item);
                    dataItemIndex++;
                    itemIndex++;
                }
            }
        }

        private GridColumnCollection CreateColumnSet(IEnumerable retrivedData)
        {
            if (this.AutoGenerateColumns)
                return this.CreateAutoGeneratedColumns(retrivedData);
            else
                return this.Columns;
        }

        private GridColumnCollection CreateAutoGeneratedColumns(IEnumerable retrivedData)
        {
            GridColumnCollection columnCollection = null;
            object firstDataItem = null;
            IEnumerator enumerator = retrivedData.GetEnumerator();
            if (enumerator != null)
            {
                if (enumerator.MoveNext())
                    firstDataItem = enumerator.Current;
            }
            if (firstDataItem != null)
            {
                PropertyDescriptorCollection properties = TypeDescriptor.GetProperties(firstDataItem);
                if (properties != null)
                {
                    columnCollection = new GridColumnCollection();
                    for (int index = 0; index < properties.Count; index++)
                    {
                        PropertyDescriptor descriptor = properties[index];
                        GridColumn column = new GridColumn();
                        column.HeaderText = descriptor.Name;
                        column.DataField = descriptor.Name;
                        column.ColumnIndex = index;
                        columnCollection.Add(column);
                    }
                }
            }
            return columnCollection;
        }

        private GridItem CreateItem(int itemIndex, object dataItem)
        {
            GridItem item = new GridItem(itemIndex, dataItem);
            GridItemEventArgs args = new GridItemEventArgs(item);
            this.InitializeItem(item);

            item.DataItem = dataItem;
            this.OnItemCreated(args);
            item.DataBind();
            this.OnItemDataBound(args);

            return item;
        }

        private object GetFieldValue(GridItem item, string fieldName)
        {
            object retVal = null;
            PropertyDescriptorCollection properties = TypeDescriptor.GetProperties(item.DataItem);
            if (properties != null)
            {
                foreach (PropertyDescriptor descriptor in properties)
                {
                    if (descriptor.Name == fieldName)
                    {
                        retVal = descriptor.GetValue(item.DataItem);
                        break;
                    }
                }
            }
            return retVal;
        }

        protected void InitializeItem(GridItem item)
        {
            ITemplate template = null;
            if (template != null)
                template.InstantiateIn(item);
        }

        protected override void Render(HtmlTextWriter writer)
        {
            if (!this.DesignMode)
            {
                ScriptManager scriptManager = ScriptManager.GetCurrent(Page);
                scriptManager.RegisterScriptDescriptors(this);
            }

            writer.AddAttribute(HtmlTextWriterAttribute.Id, this.ClientID);
            writer.RenderBeginTag(HtmlTextWriterTag.Div);

            this.RenderTable(writer);
            this.RenderSelectionRect(writer);
            this.RenderFocusRect(writer);

            writer.RenderEndTag();
        }

        private void RenderTable(HtmlTextWriter writer)
        {
            if (this._innerColumnCollection != null)
            {
                writer.AddAttribute(HtmlTextWriterAttribute.Cellpadding, Constants.CellPadding);
                writer.AddAttribute(HtmlTextWriterAttribute.Cellspacing, Constants.CellSpacing);
                writer.AddAttribute(HtmlTextWriterAttribute.Class, Constants.TableCssClass);
                writer.RenderBeginTag(HtmlTextWriterTag.Table);

                writer.RenderBeginTag(HtmlTextWriterTag.Thead);
                writer.RenderBeginTag(HtmlTextWriterTag.Tr);

                writer.AddAttribute(HtmlTextWriterAttribute.Class, Constants.RowHeaderCssClass);
                writer.RenderBeginTag(HtmlTextWriterTag.Th);
                writer.RenderEndTag();

                foreach (GridColumn column in this._innerColumnCollection)
                {
                    writer.AddAttribute(HtmlTextWriterAttribute.Width, Constants.RowHeaderWidth);
                    writer.AddAttribute(HtmlTextWriterAttribute.Class, Constants.ColumnHeaderCssClass);
                    writer.RenderBeginTag(HtmlTextWriterTag.Th);
                    writer.Write(column.HeaderText);
                    writer.RenderEndTag();
                }
                writer.RenderEndTag();
                writer.RenderEndTag();

                writer.RenderBeginTag(HtmlTextWriterTag.Tbody);
                foreach (GridItem item in this._rowItemCollection)
                {
                    if (item.DisplayIndex % 2 == 0)
                        writer.AddStyleAttribute(HtmlTextWriterStyle.BackgroundColor, Constants.EvenRowBackgroundColor);
                    else
                        writer.AddStyleAttribute(HtmlTextWriterStyle.BackgroundColor, Constants.OddRowBackgroundColor);
                    writer.RenderBeginTag(HtmlTextWriterTag.Tr);

                    writer.AddAttribute(HtmlTextWriterAttribute.Width, Constants.RowHeaderWidth);
                    writer.AddAttribute(HtmlTextWriterAttribute.Class, Constants.RowHeaderCssClass);
                    writer.RenderBeginTag(HtmlTextWriterTag.Th);
                    writer.RenderEndTag();

                    foreach (GridColumn column in this._innerColumnCollection)
                    {
                        writer.RenderBeginTag(HtmlTextWriterTag.Td);
                        writer.Write(this.GetFieldValue(item, column.DataField) ?? string.Empty);
                        writer.RenderEndTag();
                    }
                    writer.RenderEndTag();
                }
                writer.RenderEndTag();
                writer.RenderEndTag();
            }
        }

        private void RenderSelectionRect(HtmlTextWriter writer)
        {
            writer.AddAttribute(HtmlTextWriterAttribute.Class, Constants.SelectionRectInactiveCssClass);
            writer.RenderBeginTag(HtmlTextWriterTag.Div);
            writer.RenderEndTag();
        }

        private void RenderFocusRect(HtmlTextWriter writer)
        {
            writer.AddAttribute(HtmlTextWriterAttribute.Class, Constants.SelectionRectInactiveCssClass);
            writer.RenderBeginTag(HtmlTextWriterTag.Div);
            writer.RenderEndTag();
        }

        private void OnItemCreated(GridItemEventArgs e)
        {
            GridItemEventHandler handler = (GridItemEventHandler)base.Events[Grid.EventItemCreated];
            if (handler != null)
                handler(this, e);
        }

        private void OnItemDataBound(GridItemEventArgs e)
        {
            GridItemEventHandler handler = (GridItemEventHandler)base.Events[Grid.EventItemDataBound];
            if (handler != null)
                handler(this, e);
        }

        #endregion

        #region IScriptControl Members

        public IEnumerable<ScriptDescriptor> GetScriptDescriptors()
        {
            ScriptControlDescriptor descriptor = new ScriptControlDescriptor("Al.UI.Controls.Grid", this.ClientID);
            descriptor.AddProperty("selectionMode", this.SelectionMode);
            descriptor.AddProperty("columns", this.Columns);

            return new ScriptDescriptor[] { descriptor };
        }

        public IEnumerable<ScriptReference> GetScriptReferences()
        {
            ScriptReference reference = new ScriptReference();
            if (Page != null)
                reference.Path = Page.ClientScript.GetWebResourceUrl(this.GetType(), "Al.UI.Controls.Grid.Grid.js");

            return new ScriptReference[] { reference };
        }

        #endregion

        #region IPostBackDataHandler Members

        public bool LoadPostData(string postDataKey, System.Collections.Specialized.NameValueCollection postCollection)
        {
            return true;
        }

        public void RaisePostDataChangedEvent()
        {
        }

        #endregion
    }
}